/*
 * Copyright 2021-present Open Networking Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onosproject.ngsdn.tutorial;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.net.DeviceId;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.ngsdn.tutorial.common.DerviceFlowLink;
import org.onosproject.ngsdn.tutorial.common.FlowLink;
import org.onosproject.rest.AbstractWebResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.PathParam;
import javax.ws.rs.DELETE;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static org.onlab.util.Tools.nullIsIllegal;
import static org.onlab.util.Tools.readTreeFromStream;

/**
 * Sample web resource.
 */
@Path("secret_transmission")
public class AppWebResource extends AbstractWebResource {

    /**
     * Get hello world greeting.
     *
     * @return 200 OK
     */
    private final Logger log = LoggerFactory.getLogger(getClass());
    private static final String FLOW_ARRAY_REQUIRED = "Flows array was not specified";
    private static final String FLOWS = "flows";
    private final RouterComponent service = get(RouterComponent.class);
//    private final PacketCount packetCount = get(PacketCount.class);

    @GET
    @Path("ModalChange")
    public Response getSchema(@QueryParam("modal") String modal)  {
        RouterComponent.ReturnMessage rm = service.switchModal(modal);
        ObjectNode node = mapper().createObjectNode();
        node.put("Status", rm.getStatus());
        node.put("Message", rm.getMessage());
        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
    }
    @GET
    @Path("ModalChangeMany")
    public Response ChangeManyModal()  {
        RouterComponent.ReturnMessage rm = service.switchManyModal();
        ObjectNode node = mapper().createObjectNode();
        node.put("Status", rm.getStatus());
        node.put("Message", rm.getMessage());
        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
    }
    @POST
    @Path("{deviceId}")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
//    @Path("setDeviceFlow")
    public Response setDeviceFlow(@PathParam("deviceId") String deviceId,InputStream stream)  {
        try {
            ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
            log.info("jsonTree is  {} ",jsonTree);
            JsonNode specifiedDeviceId = jsonTree.get("deviceId");
            if (specifiedDeviceId != null &&
                    !specifiedDeviceId.asText().equals(deviceId)) {
                throw new IllegalArgumentException(
                        "Invalid deviceId in flow creation request");
            }
            JsonNode flowLinks = jsonTree.get("flowLinks");
            List<FlowLink> asFlowLinks = new ArrayList<>();
            for (int i = 0; i < flowLinks.size(); i++){
                JsonNode flowLink = flowLinks.get(i);
                log.info("flowlink{} is  {} ",i,flowLink);
                JsonNode srcIP = flowLink.get("srcIP");
                JsonNode dstIP = flowLink.get("dstIP");
                JsonNode post = flowLink.get("post");
                FlowLink asFlowLink = new FlowLink();
                asFlowLink.setSrcIP(srcIP.asText());
                asFlowLink.setDstIP(dstIP.asText());
                asFlowLink.setPost(post.asLong());
                asFlowLinks.add(asFlowLink);
            }
            log.info("flowlink is  {} ",flowLinks);
            DerviceFlowLink derviceFlowLink = new DerviceFlowLink();
            derviceFlowLink.setFlowLinks(asFlowLinks);
            derviceFlowLink.setDeviceId(DeviceId.deviceId(specifiedDeviceId.asText()));
//            mapper().readValue(stream,DerviceFlowLink.class);
//            DerviceFlowLink deviceFlowLink = codec(DerviceFlowLink.class).decode(jsonTree, this);
            log.info("deviceFlowLink is ok ");
            RouterComponent.ReturnMessage rm = service.setDeviceFlow(derviceFlowLink);
            ObjectNode node = mapper().createObjectNode();
            node.put("Status", rm.getStatus());
            node.put("Message", rm.getMessage());
            return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
        } catch (IOException ex) {
            throw new IllegalArgumentException(ex);
        }
    }
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
//    @Path("setDeviceFlow")
    public Response setAllDeviceFlow(InputStream stream)  {
        try {
            ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
            ArrayNode flowsArray = nullIsIllegal((ArrayNode) jsonTree.get(FLOWS),
                    FLOW_ARRAY_REQUIRED);
            log.info("flowsArray is  {} ",flowsArray);

            List<DerviceFlowLink> flowLinks = new ArrayList<>();
            for (int i = 0; i < flowsArray.size(); i++){
                JsonNode jsonFlowLinks = flowsArray.get(i).get("flowLinks");
                log.info("flowlink is  {} ",jsonFlowLinks);
                JsonNode specifiedDeviceId = flowsArray.get(i).get("deviceId");
                log.info("specifiedDeviceId is  {} ",specifiedDeviceId);
                List<FlowLink> asFlowLinks = new ArrayList<>();
                for (int j =0; j< jsonFlowLinks.size();j++){
                    JsonNode flowLink = jsonFlowLinks.get(j);
                    log.info("flowlink{} is  {} ",j,flowLink);
                    JsonNode srcIP = flowLink.get("srcIP");
                    JsonNode dstIP = flowLink.get("dstIP");
                    JsonNode post = flowLink.get("post");
                    FlowLink asFlowLink = new FlowLink();
                    asFlowLink.setSrcIP(srcIP.asText());
                    asFlowLink.setDstIP(dstIP.asText());
                    asFlowLink.setPost(post.asLong());
                    asFlowLinks.add(asFlowLink);
                }
                DerviceFlowLink derviceFlowLink = new DerviceFlowLink();
                derviceFlowLink.setFlowLinks(asFlowLinks);
                derviceFlowLink.setDeviceId(DeviceId.deviceId(specifiedDeviceId.asText()));
                flowLinks.add(derviceFlowLink);
//                log.info("flowlink is  {} ",flowLinks.toString());
            }


//            flowsArray.forEach(flowJson);
//            List<DerviceFlowLink> flowLinks = codec(DerviceFlowLink.class).decode(flowsArray, this);
            RouterComponent.ReturnMessage rm = service.setAllDeviceFlow(flowLinks);
            ObjectNode node = mapper().createObjectNode();
            node.put("Status", rm.getStatus());
            node.put("Message", rm.getMessage());
            return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
        } catch (IOException ex) {
            throw new IllegalArgumentException(ex);
        }
    }
    @GET
    @Path("creatSecretTransmissionOneLink")
    public Response creatSecretTransmissionOneLink(@QueryParam("srcIP") String srcIP,@QueryParam("dstIP") String dstIP,@QueryParam("modal") String modal)  {
        RouterComponent.ReturnMessage rm = service.creatSecretTransmissionOneLink(srcIP,dstIP,modal);
        ObjectNode node = mapper().createObjectNode();
        node.put("Status", rm.getStatus());
        node.put("Message", rm.getMessage());
        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
    }
    @GET
    @Path("creatSecretTransmissionManyLink")
    public Response creatSecretTransmissionManyLink(@QueryParam("srcIP") String srcIP,@QueryParam("dstIP") String dstIP,@QueryParam("modal1") String modal1,@QueryParam("modal2") String modal2)  {
        RouterComponent.ReturnMessage rm = service.creatSecretTransmissionManyLink(srcIP,dstIP,modal1,modal2);
        ObjectNode node = mapper().createObjectNode();
        node.put("Status", rm.getStatus());
        node.put("Message", rm.getMessage());
        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
    }
    @GET
    @Path("deleteSecretTransmissionLink")
    public Response deleteSecretTransmissionLink(@QueryParam("srcIP") String srcIP,@QueryParam("dstIP") String dstIP)  {
        RouterComponent.ReturnMessage rm = service.deleteSecretTransmissionLink(srcIP,dstIP);
        ObjectNode node = mapper().createObjectNode();
        node.put("Status", rm.getStatus());
        node.put("Message", rm.getMessage());
        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
    }
    @GET
    @Path("getCurrentModal")
    public Response Start()  {
        String modal = service.getCurrentModal();
        ObjectNode node = mapper().createObjectNode().put("current modal", modal);
        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
    }

    @GET
    @Path("deleteAllModelRule")
    public Response deleteAllModelRule()  {
        RouterComponent.ReturnMessage rm = service.deleteAllModelRule();
        ObjectNode node = mapper().createObjectNode();
        node.put("Status", rm.getStatus());
        node.put("Message", rm.getMessage());
        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
    }
    @DELETE
    @Path("deleteAllRule")
    public Response deleteAllRule()  {
        RouterComponent.ReturnMessage rm = service.deleteAllRule();
        ObjectNode node = mapper().createObjectNode();
        node.put("Status", rm.getStatus());
        node.put("Message", rm.getMessage());
        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
    }
//    @GET
//    @Path("encrypt")
//    public Response Encrypt(@QueryParam("crypt") String crypt)  {
//        if (crypt.equals("true")) {
//            service.enableEncrypt();
//        } else if (crypt.equals("false")) {
//            service.disableEncrypt();
//        }
//        ObjectNode node = mapper().createObjectNode().put("encryptState", crypt);
//        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
//    }

    @GET
    @Path("getEncryptStatus")
    public Response getEncryptStatus() {
        ObjectNode node = mapper().createObjectNode().put("encryptState", service.encrypted);
        log.info("encryptState:",Boolean.toString(service.encrypted));
        return Response.ok(node, MediaType.APPLICATION_JSON_TYPE).build();
    }

//    @GET
//    @Path("PortStats")
//    public Response PortStats(@QueryParam("DeviceId") String deviceId) {
//        List<PacketCount.PortStats> portStatsList =
//                packetCount.getDeviceCount(DeviceId.deviceId(deviceId));
//
//        ObjectMapper mapper = new ObjectMapper();
//        ObjectNode root = mapper.createObjectNode();
//        ArrayNode arrayNode = mapper.createArrayNode();
//
//        for(PacketCount.PortStats ps : portStatsList) {
//            ObjectNode node = mapper.createObjectNode();
//            node.put("deviceId", deviceId);
//            node.put("packetReceive", ps.packetReceived);
//            node.put("packetSend", ps.packetSend);
//            node.put("byteReceive", ps.byteReceived);
//            node.put("byteSend", ps.byteSend);
//            node.put("port", ps.port);
//
//            arrayNode.add(node);
//        }
//        root.set("PortStats", arrayNode);
//        return Response.ok(root.toString(), MediaType.APPLICATION_JSON_TYPE).build();
//    }
}
